(function(window, document) {
// change this to relative path (relative to script) or to absolute path
// where the audio, fonts and images folders are located
// (include a slash at the end)
var RESOURCES_FOLDER_PATH = "";
var requestAnimationFrame = (function() {
if (window.requestAnimationFrame) return window.requestAnimationFrame;
if (window.oRequestAnimationFrame) return window.oRequestAnimationFrame;
if (window.msRequestAnimationFrame) return window.msRequestAnimationFrame;
if (window.mozRequestAnimationFrame) return window.mozRequestAnimationFrame;
return function(callback) {
setTimeout(callback, 1000 / 60);
}
})();
window.requestAnimFrame = requestAnimationFrame;
var link = document.createElement("link");
link.setAttribute("rel", "stylesheet");
link.setAttribute("href", RESOURCES_FOLDER_PATH + "css/spiderman-game.css");
document.head.appendChild(link);
var RESOURCES = {
"JUMP" : "images/jump.png",
"RUNNING_CHANGE_STEP": "images/running-change-step.png",
"RUNNING_LEFT_STEP" : "images/running-left-step.png",
"RUNNING_RIGHT_STEP" : "images/running-right-step.png",
"SHOOT_CHANGE_STEP" : "images/shoot-change-step.png",
"SHOOT_JUMP" : "images/shoot-jump.png",
"SHOOT_LEFT-STEP" : "images/shoot-left-step.png",
"SHOOT_RIGHT-STEP" : "images/shoot-right-step.png",
"SHOOT" : "images/shoot.png",
"SLIDE" : "images/slide.png",
"STANDING" : "images/standing.png",
"WEB_PROJECTILE" : "images/web.png",
"BACKGROUND" : "images/background.jpg",
"ROOF" : "images/wall.jpg",
"BUILDING" : "images/building.png",
"SPIDER_HEAD" : "images/spider-head.png",
"HEART" : "images/heart.png",
"VENOM" : "images/venom.png",
"THUG" : "images/thug.png",
"KNIFE" : "images/knife.png",
};
var AUDIO_RESOURCES = {
"AMAZING_SPIDER_MAN_2" : new Audio(RESOURCES_FOLDER_PATH + "audio/amazing-spider-man-2.mp3"),
"FRIENDLY_SPIDERMAN" : new Audio(RESOURCES_FOLDER_PATH + "audio/60-theme-song.mp3"),
"MOVIE_THEME" : new Audio(RESOURCES_FOLDER_PATH + "audio/old-theme.mp3"),
"ANIMATED_SERIES" : new Audio(RESOURCES_FOLDER_PATH + "audio/animated-series-theme.mp3"),
"SHOOT" : new Audio(RESOURCES_FOLDER_PATH + "audio/shooting-web.mp3"),
};
var AUDIO_LOOP = [
"AMAZING_SPIDER_MAN_2",
"FRIENDLY_SPIDERMAN",
"MOVIE_THEME",
"ANIMATED_SERIES",
];
var KEY = {
ARROW_LEFT: 37,
ARROW_UP: 38,
ARROW_RIGHT: 39,
ARROW_DOWN: 40,
SPACEBAR: 32,
A: 65,
S: 87,
D: 68,
W: 87,
ESC: 27,
};
var DIRECTION = {
RIGHT: 1,
LEFT: -1,
}
function SpidermanGame(opts) {
var options = {
canvas: "canvas",
score: 0,
muted: false,
soundEffects: true,
};
opts = opts || {};
for (var option in options) {
if (opts.hasOwnProperty(option)) {
options[option] = opts[option];
}
this[option] = options[option];
}
// how many frames have passed
this.frame = 0;
this.resources = {};
this.cameraX = 0;
this.score = this.score || 0;
this.scene = {
spiderman: null,
projectiles: [],
roofs: [],
enemies: [],
}; // object that contains information about the next scene
}
SpidermanGame.prototype.paused = false;
SpidermanGame.prototype.initialized = false;
SpidermanGame.prototype.soundEffects = true;
SpidermanGame.prototype.escapeKey = false;
SpidermanGame.prototype.muted = false;
SpidermanGame.prototype.slowmotion = false;
SpidermanGame.prototype.load = function() {
if (this.initialized) return false;
var self = this;
this.canvas = document.querySelector(this.canvas);
if (!this.canvas) {
this.canvas = document.createElement("canvas");
document.body.appendChild(this.canvas);
}
this.ctx = this.canvas.getContext("2d");
this.canvas.height = 400;
this.canvas.width = 711;
var menu = document.createElement("div");
menu.innerHTML =
'<div class="spiderman-game-menu-container">' +
'<div class="spiderman-game-menu-title">PAUSED</div>' +
'<div class="spiderman-game-menu-button spiderman-game-menu-button-resume">RESUME</div>' +
'<div class="spiderman-game-menu-button spiderman-game-menu-button-mute-sounds">MUTE SOUNDS</div>' +
'<div class="spiderman-game-menu-button spiderman-game-menu-button-mute-music">MUTE MUSIC</div>' +
'<div class="spiderman-game-menu-button spiderman-game-menu-button-mute-slowmotion">TOGGLE SLOWMOTION</div>' +
'</div>';
menu = menu.firstChild;
menu.style.display = "none";
menu.querySelector(".spiderman-game-menu-button-resume").onclick = function() {
self.unpause();
}
menu.querySelector(".spiderman-game-menu-button-mute-sounds").onclick = function() {
if (self.soundEffects) {
self.soundEffects = false;
this.innerHTML = "UNMUTE SOUNDS";
} else {
self.soundEffects = true;
this.innerHTML = "MUTE SOUNDS";
}
}
menu.querySelector(".spiderman-game-menu-button-mute-music").onclick = function() {
if (self.muted) {
self.unmute();
this.innerHTML = "MUTE MUSIC";
} else {
self.mute();
this.innerHTML = "UNMUTE MUSIC";
}
}
menu.querySelector(".spiderman-game-menu-button-mute-slowmotion").onclick = function() {
if (self.slowmotion) {
self.setSlowmotion(false);
} else {
self.setSlowmotion(true);
}
}
document.body.appendChild(menu);
this.pauseMenu = menu;
var gameoverMenu = document.createElement("div");
gameoverMenu.innerHTML =
'<div class="spiderman-game-menu-container">' +
'<div class="spiderman-game-menu-title">GAME OVER</div>' +
'<div class="spiderman-game-menu-title">FINAL SCORE: <span class="spiderman-game-score">0</span></div>' +
'<div class="spiderman-game-menu-button spiderman-game-menu-button-restart">RESTART</div>' +
'</div>';
gameoverMenu = gameoverMenu.firstChild;
gameoverMenu.querySelector(".spiderman-game-menu-button-restart").onclick = function() {
self.restart();
}
document.body.appendChild(gameoverMenu);
this.gameoverMenu = gameoverMenu;
var spiderman = new SpiderMan(this);
this.spiderman = spiderman;
document.addEventListener("keydown", function(e) {
var keyCode = e.keyCode || e.which;
// fire this on the FIRST keydown callback
if (keyCode == KEY.ESC && !self.escapeKey) {
self.escapeKey = true;
if (self.paused) {
self.unpause();
} else {
self.pause();
}
}
self.spiderman.keydown(e.keyCode || e.which);
});
document.addEventListener("keyup", function(e) {
var keyCode = e.keyCode || e.which;
if (keyCode == KEY.ESC) {
self.escapeKey = false;
}
self.spiderman.keyup(keyCode);
});
window.addEventListener("resize", function() {
// resizing might change the canvas position, re position the menu if it is visible
if (self.paused) {
self.showPauseMenu();
}
if (self.gameIsOver) {
self.showGameoverMenu();
}
});
for (var i = 0; i < AUDIO_LOOP.length; i++) {
var soundName = AUDIO_LOOP[i];
var sound = AUDIO_RESOURCES[soundName];
sound.setAttribute("data-name", soundName);
sound.ontimeupdate = function() {
if (this.currentTime >= this.duration) {
var current = AUDIO_LOOP.indexOf(this.getAttribute("data-name"));
var next = (current + 1) % (AUDIO_LOOP.length);
self.playSound(AUDIO_LOOP[next], false, 0);
}
}
}
// to show that canvas is here, but is being loaded
this.canvas.style.backgroundColor = "black";
this.ctx.font = "30px Helvetica";
this.ctx.textAlign = "center";
this.ctx.fillStyle = "white";
this.ctx.fillText("Loading...", this.canvas.width / 2, this.canvas.height / 2);
return new Promise(function(resolve, reject) {
var reourcesArray = [];
for (var resource in RESOURCES) {
reourcesArray.push({
name: resource,
source: RESOURCES_FOLDER_PATH + RESOURCES[resource],
});
}
var index = 0;
function loadNext() {
if (!reourcesArray[index]) {
var roof = new Roof(self, 0);
self.scene.spiderman = spiderman;
self.scene.roofs = [roof];
self.update();
self.playSound(AUDIO_LOOP[0], false, 0);
// if game was muted in initial options
if (self.muted) self.mute();
return resolve();
}
var resource = reourcesArray[index];
var img = new Image();
img.onload = function() {
index++;
self.resources[resource.name] = img;
loadNext();
}
img.src = resource.source;
}
loadNext();
});
}
SpidermanGame.prototype.setSlowmotion = function(slowmo) {
if (slowmo) {
this.slowmotion = true;
window.requestAnimFrame = function(callback) {
setTimeout(callback, 1000 / 10);
}
for (var audio in AUDIO_RESOURCES) {
AUDIO_RESOURCES[audio].playbackRate = 0.5;
}
} else {
this.slowmotion = false;
window.requestAnimFrame = requestAnimationFrame;
for (var audio in AUDIO_RESOURCES) {
AUDIO_RESOURCES[audio].playbackRate = 1;
}
}
}
SpidermanGame.prototype.mute = function() {
this.muted = true;
for (var audio in AUDIO_RESOURCES) {
AUDIO_RESOURCES[audio].volume = 0;
}
}
SpidermanGame.prototype.unmute = function() {
this.muted = false;
for (var audio in AUDIO_RESOURCES) {
AUDIO_RESOURCES[audio].volume = 1;
}
}
SpidermanGame.prototype.showPauseMenu = function() {
if (this.gameoverMenu.style.display == "block") return;
var pauseMenu = this.pauseMenu;
var canvasRect = this.canvas.getBoundingClientRect(); // includes CSS translations
var left = canvasRect.left;
var top = canvasRect.top;
this.pauseMenu.style.display = "block";
this.pauseMenu.style.left = (left + this.canvas.width / 2) + "px";
this.pauseMenu.style.top = (top + this.canvas.height / 2) + "px";
}
SpidermanGame.prototype.showGameoverMenu = function() {
var gameoverMenu = this.gameoverMenu;
this.gameoverMenu.querySelector(".spiderman-game-score").innerHTML = this.score;
var canvasRect = this.canvas.getBoundingClientRect(); // includes CSS translations
var left = canvasRect.left;
var top = canvasRect.top;
this.gameoverMenu.style.display = "block";
this.gameoverMenu.style.left = (left + this.canvas.width / 2) + "px";
this.gameoverMenu.style.top = (top + this.canvas.height / 2) + "px";
}
SpidermanGame.prototype.pause = function() {
this.paused = true;
this.showPauseMenu();
};
SpidermanGame.prototype.unpause = function() {
this.paused = false;
this.pauseMenu.style.display = "none";
this.update();
}
SpidermanGame.prototype.playSound = function(audio, clone, currentTime) {
audio = audio && audio.play ? audio : AUDIO_RESOURCES[audio];
if (audio && audio.play) {
if (clone) {
audio = audio.cloneNode(true);
}
if (currentTime != undefined) {
audio.currentTime = currentTime;
}
return audio.play();
}
};
SpidermanGame.prototype.pauseSound = function(audio) {
if (audio && audio.pause) {
return audio.pause();
}
if (AUDIO_RESOURCES[audio]) {
AUDIO_RESOURCES[audio].pause();
}
}
SpidermanGame.prototype.drawBackground = function() {
var background = this.resources.BACKGROUND;
var backgroundWidth = background.width;
var backgroundHeight = background.height;
var x = this.cameraX / 5 * -1;
var y = 0;
x %= Math.min(background.width, this.canvas.width);
var ratio = backgroundWidth / backgroundHeight;
this.ctx.drawImage(background, x, y, this.canvas.height * ratio, this.canvas.height);
this.ctx.drawImage(background, x + this.canvas.height * ratio, y, this.canvas.height * ratio, this.canvas.height);
}
SpidermanGame.prototype.drawRoofs = function() {
var roofs = this.scene.roofs;
for (var i = 0; i < roofs.length; i++) {
roofs[i].update();
}
// if roof left the frame and was removed, add another one
if (roofs.length < 3) {
var lastRoof = roofs[roofs.length - 1];
var x = lastRoof.x + lastRoof.fullWidth + Math.round(Math.random() * 50) + 100;
var roof = new Roof(this, x);
this.addRoof(roof);
roofs[0].update();
}
}
SpidermanGame.prototype.drawEnemies = function() {
var enemies = this.scene.enemies;
for (var i = 0; i < enemies.length; i++) {
enemies[i].update();
}
}
SpidermanGame.prototype.update = function() {
if (this.paused) return;
if (this.gameIsOver) return;
// draw the scene
var scene = this.scene;
var spiderman = scene.spiderman;
var projectiles = scene.projectiles;
// clear the canvas for re drawing
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
this.drawBackground();
this.drawRoofs();
this.drawEnemies();
for (var i = 0; i < projectiles.length; i++) {
projectiles[i].update();
}
spiderman.update();
this.ctx.fillStyle = "white";
this.ctx.font = "20px SpidermanGamePixelFont, Monospace, Helvetica";
this.ctx.textAlign = "center";
this.ctx.textBaseline = "top";
this.ctx.fillText(this.score, this.canvas.width / 2, 5);
for (var i = 0; i < projectiles.length; i++) {
var projectile = projectiles[i];
var x = projectile.x;
var y = projectile.y;
var character = this.isCharacterAtPoint(x, y);
if (character) {
projectile.handleHitWithCharacter(character);
character.handleHitWithProjectile(projectile);
}
}
requestAnimFrame(this.update.bind(this));
}
SpidermanGame.prototype.addProjectile = function(projectile) {
if (projectile instanceof Projectile) {
this.scene.projectiles.push(projectile);
}
}
SpidermanGame.prototype.removeProjectile = function(projectile) {
var projectiles = this.scene.projectiles;
if (projectiles.indexOf(projectile) > -1) {
projectiles.splice(projectiles.indexOf(projectile), 1);
}
}
SpidermanGame.prototype.addEnemy = function(enemy) {
if (enemy instanceof Enemy) {
this.scene.enemies.push(enemy);
}
}
SpidermanGame.prototype.removeEnemy = function(enemy) {
var enemies = this.scene.enemies;
if (enemies.indexOf(enemy) > -1) {
enemies.splice(enemies.indexOf(enemy), 1);
}
}
SpidermanGame.prototype.addRoof = function(roof) {
if (roof instanceof Roof) {
this.scene.roofs.push(roof);
}
}
SpidermanGame.prototype.removeRoof = function(roof) {
var roofs = this.scene.roofs;
if (roofs.indexOf(roof) > -1) {
roofs.splice(roofs.indexOf(roof), 1);
}
}
// checks if given point is roof
SpidermanGame.prototype.isRoofAtPoint = function(x, y) {
x -= this.cameraX; // to move point relative to canvas
for (var i = 0; i < this.scene.roofs.length; i++) {
var roof = this.scene.roofs[i];
// since character is relative to the camera, calculate X of roof relative to camera as well
var roofX = roof.x - this.cameraX;
if (roofX <= x && roofX + roof.fullWidth >= x && y >= roof.y) return roof;
}
return false;
}
SpidermanGame.prototype.isCharacterAtPoint = function(x, y) {
// enemies + spiderman
var characters = this.scene.enemies.concat(this.spiderman);
x -= this.cameraX;
for (var i = 0; i < characters.length; i++) {
var character = characters[i];
var stateImg = character.stateImg || {};
var left = character.x - this.cameraX;
var top = character.y;
var right = left + stateImg.width * character.scale;
var bottom = top + stateImg.height * character.scale;
var isCharacter =
left <= x // check left bound
&& top <= y // top bound
&& right >= x // right bound
&& bottom >= y; // bottom bound
if (isCharacter) return character;
}
return false;
}
SpidermanGame.prototype.restart = function() {
var roof = new Roof(this);
roof.x = 0;
this.spiderman = new SpiderMan(this);
this.scene.spiderman = this.spiderman;
this.scene.projectiles = [];
this.scene.roofs = [roof];
this.scene.enemies = [];
this.cameraX = 0;
this.score = 0;
this.paused = false;
this.gameIsOver = false;
this.gameoverMenu.style.display = "none";
this.pauseMenu.style.display = "none";
this.update();
}
SpidermanGame.prototype.gameover = function() {
this.gameIsOver = true;
}
function SpiderMan(game) {
this.game = game;
this.canvas = game.canvas;
this.ctx = game.ctx;
this.name = "SPIDER_MAN";
this.x = 0;
this.y = 0;
this.states = ["STANDING"];
this.scale = 0.5;
this.keydowns = [];
this.health = 5;
this.maxHealth = 5;
this.web = 50;
this.velocityX = 0;
this.velocityY = 0;
// to regenerate every N fps (approximately N / 60 seconds)
this.regenerationSpeed = 1200;
// how many frames have passed
this.frame = 0;
this.runningFrames = ["RUNNING_RIGHT_STEP", "RUNNING_CHANGE_STEP", "RUNNING_LEFT_STEP", "RUNNING_CHANGE_STEP"];
this.runningShootingFrames = ["SHOOT_RIGHT-STEP", "SHOOT_CHANGE_STEP", "SHOOT_LEFT-STEP", "SHOOT_CHANGE_STEP"];
this.runningFrame = 0;
this.gravityForce = 0.7;
this.runningDirection = 0;
this.runningSpeed = 5;
this.shootingFrame = 0;
this.wasDamagedOnPreviousFrame = false;
}
SpiderMan.prototype.keyIsDown = function(keyCode) {
return this.keydowns.indexOf(keyCode) > -1;
}
SpiderMan.prototype.hasState = function(state) {
return this.states.indexOf(state) > -1;
}
SpiderMan.prototype.hasStates = function(states) {
states = states.split(" ");
var hasStates = true;
for (var i = 0; i < states.length; i++) {
hasState = hasState && this.hasState(states[i]);
}
return hasStates;
}
SpiderMan.prototype.addState = function(state) {
if (this.hasState(state) === false) this.states.push(state);
}
SpiderMan.prototype.removeState = function(state) {
if (state instanceof Array) {
for (var i = 0; i < state.length; i++) {
this.removeState(state[i]);
}
}
if (this.hasState(state)) this.states.splice(this.states.indexOf(state), 1);
}
SpiderMan.prototype.handleHitWithProjectile = function(projectile) {
if (projectile.name != "WEB") {
this.health -= projectile.damage;
this.wasDamagedOnPreviousFrame = true;
}
}
// returns the image to draw in position of spiderman
SpiderMan.prototype.stateImage = function() {
var state = "STANDING";
if (this.hasState("JUMP")) {
state = "JUMP";
if (this.velocityY == 0) {
this.velocityY = -15;
}
}
if (this.velocityY >= 0) this.removeState("JUMP");
if (this.hasState("RUNNING")) {
state = this.runningFrames[this.runningFrame];
// if user is shooting while running
if (this.hasState("SHOOT")) state = this.runningShootingFrames[this.runningFrame];
// every 10th frame update the image
if (this.frame % 10 === 0) {
this.runningFrame++;
this.runningFrame %= this.runningFrames.length - 1;
}
this.velocityX = this.runningDirection * this.runningSpeed;
} else {
this.velocityX = 0;
}
if (this.hasState("SHOOT")) {
if (!this.hasState("RUNNING")) state = "SHOOT";
if (this.shootingFrame % 20 === 0) {
this.shoot(this.game.resources.SHOOT);
}
this.shootingFrame++;
}
var image = this.game.resources[state] || this.game.resources["STANDING"];
this.stateImg = image; // so that stateImage is accessible
return image;
}
SpiderMan.prototype.keydown = function(keyCode) {
this.keydowns.push(keyCode);
}
SpiderMan.prototype.keyup = function(keyCode) {
this.runningFrame = 0;
if (keyCode == KEY.ARROW_RIGHT || keyCode == KEY.ARROW_LEFT) {
this.removeState("RUNNING");
}
if (keyCode == KEY.SPACEBAR) {
this.removeState("SHOOT");
// reset the shootingFrame so if user presses space rapidly, it shoots rapidly
// but if user holds the space, it shoots every 20 frame
this.shootingFrame = 0;
}
while (this.keydowns.indexOf(keyCode) > -1) {
this.keydowns.splice(this.keydowns.indexOf(keyCode), 1);
}
}
SpiderMan.prototype.regenerate = function() {
// if this is 300th fps, regenerate
if (this.frame % this.regenerationSpeed === 0 && this.health < this.maxHealth) {
this.health = Math.round(this.health + 1);
}
}
SpiderMan.prototype.shoot = function(img) {
if (this.web <= 0) return;
var direction = this.runningDirection || 1;
var web = new Projectile(this.game);
web.name = "WEB";
web.damage = 2;
web.x = this.x + img.width * this.scale + 1;
if (this.runningDirection == DIRECTION.LEFT) {
web.x = this.x - 1; // left hand will be the X position of the spiderman
}
web.y = this.y + img.height * this.scale / 2;
web.update = function() {
var x = this.x - this.game.cameraX;
var y = this.y;
if (direction == DIRECTION.LEFT) {
// if spiderman is turned to left,
// move web by 20pixels since X coordinates start from LEFT to right
x -= 20;
}
this.ctx.drawImage(this.game.resources["WEB_PROJECTILE"], x, y - 10, 20, 20);
this.x += direction * 10;
if (this.x - this.game.cameraX >= this.canvas.width || this.x <= 0) this.remove();
}
web.handleHitWithCharacter = function(character) {
// somtimes projectile is being hit by the spiderman ON launch
// the best way is probably to launch it better but for now, i'll just make sure
// it doesn't get destroyed on launch
if (character.name != "SPIDER_MAN") return this.remove();
}
web.spiderman = this;
this.game.addProjectile(web);
if (this.game.soundEffects == true) {
this.game.playSound("SHOOT", true, 0);
}
this.web--;
}
SpiderMan.prototype.drawHealthbar = function() {
var heart = {
width: 25,
height: 25,
};
for (var i = 0; i < this.health; i++) {
// (i + 1) * 5 for every 5 pixel padding per heart
var x = i * heart.width + 5 * (i + 1);
var y = 5;
this.ctx.drawImage(this.game.resources.HEART, x, y, heart.width, heart.height);
}
}
SpiderMan.prototype.drawWebbar = function() {
var img = this.game.resources.WEB_PROJECTILE;
var string = "X " + this.web;
this.ctx.fillStyle = "white";
this.ctx.font = "15px SpidermanGamePixelFont, Monospace, Arial";
this.ctx.textAlign = "start";
this.ctx.textBaseline = "top";
var web = {
width: 20,
height: 20,
};
var text = {
string: string,
width: this.ctx.measureText(string).width,
verticalPadding: 5,
horizontalPadding: 10,
};
var x = this.canvas.width - web.width - text.width - text.horizontalPadding * 2;
var y = text.verticalPadding;
var textX = x + web.width + text.horizontalPadding;
var textY = y;
this.ctx.drawImage(img, x, y, web.width, web.height);
this.ctx.fillText(text.string, textX, textY);
}
// function that gets called with global update function
SpiderMan.prototype.update = function() {
if (this.keyIsDown(KEY.ARROW_UP) && !this.hasState("FALL")) {
this.addState("JUMP");
}
if (this.keyIsDown(KEY.ARROW_RIGHT)) {
this.addState("RUNNING");
this.runningDirection = DIRECTION.RIGHT;
}
if (this.keyIsDown(KEY.ARROW_LEFT)) {
this.addState("RUNNING");
this.runningDirection = DIRECTION.LEFT;
}
if (this.keyIsDown(KEY.SPACEBAR)) {
this.addState("SHOOT");
}
if (this.y >= this.canvas.height || !this.health || !this.web) {
this.game.gameover();
}
var img = this.stateImage();
this.velocityY += this.gravityForce;
this.y += this.velocityY;
this.x += this.velocityX;
if (this.x - this.game.cameraX < 0) {
this.x = this.game.cameraX; // dont allow going left
}
if (this.x - this.game.cameraX > 150) {
this.game.cameraX += this.velocityX;
}
// check if coordinates are on the roof, before increasing it by velocityX, because it might BE inside the building
// after velocityX
// check if left side is on the roof
var roofLeft = this.game.isRoofAtPoint(this.x - this.velocityX, this.y + img.height * this.scale + 1);
// check if right X hit the roof
var roofRight = this.game.isRoofAtPoint(this.x + img.width * this.scale - this.velocityX, this.y + img.height * this.scale + 1);
// check if spiderman is standing on the ground (roof)
if (roofLeft || roofRight) {
var roof = roofLeft || roofRight;
// since velocity might kinda make the spiderman go INSIDE the wall by adding too much Y,
// we'll just check if spider's y is INSIDE the wall just because of the velocityY
// just to make sure that spider is actually on the roof
if (roof.y + this.velocityY <= this.y) {
this.x -= this.velocityX;
this.velocityX = 0;
} else {
this.y = this.canvas.height - roof.height - img.height * this.scale;
this.velocityY = 0;
this.removeState("FALL");
}
}
var x = this.x - this.game.cameraX;
var y = this.y;
var width = img.width * this.scale;
var height = img.height * this.scale;
this.ctx.save();
// if the spiderman is running to the left, flip him
if (this.runningDirection == DIRECTION.LEFT) {
this.ctx.scale(-1, 1);
x *= -1;
x -= width;
}
this.ctx.drawImage(img, x, y, width, height);
this.ctx.restore();
if (this.wasDamagedOnPreviousFrame) {
this.wasDamagedOnPreviousFrame = false;
this.ctx.fillStyle = "rgba(0, 0, 0, 0.2)";
this.ctx.fillRect(x, y, width, height);
}
this.regenerate();
this.drawHealthbar();
this.drawWebbar();
this.frame++;
}
function Projectile(game) {
this.x = 0;
this.y = 0;
this.damage = 0;
this.name = "UNKNOWN";
this.canvas = game.canvas;
this.ctx = game.ctx;
this.game = game;
}
Projectile.prototype.update = function() {
}
Projectile.prototype.remove = function() {
this.game.removeProjectile(this);
}
Projectile.prototype.handleHitWithCharacter = function() {
this.remove();
}
function Roof(game, x, y) {
this.game = game;
this.canvas = game.canvas;
this.ctx = game.ctx;
this.width = Math.round(Math.random() * (this.game.resources.BUILDING.width - 200)) + 200;
this.height = Math.round(Math.random() * 50) + 100;
this.fullWidth = this.width + 15; // 15 pixels for right end of the roof top
this.x = x || 0;
this.y = this.canvas.height - this.height;
// 70% chance?
var shouldSpawnEnemy = Math.round(Math.random() * 100) >= 30;
if (shouldSpawnEnemy) {
var enemy = new Enemy(this.game, {
x: this.x + this.width / 2,
});
enemy.y = this.y - 1 - enemy.stateImg.height * enemy.scale;
this.game.addEnemy(enemy);
this.enemy = enemy;
}
}
Roof.prototype.update = function() {
var renderX = this.x - this.game.cameraX;
var roof = this.game.resources.BUILDING;
this.ctx.drawImage(roof, 0, 0, this.width, this.height, renderX, this.y, this.width, this.height);
this.ctx.drawImage(roof, this.width, 0, 15, 26, renderX + this.width, this.y, 15, 26);
if (renderX + this.width <= 0) {
this.game.removeRoof(this);
this.game.removeEnemy(this.enemy);
}
}
function Enemy(game, opts) {
opts = opts || {};
this.game = game;
this.canvas = game.canvas;
this.ctx = game.ctx;
this.health = opts.health || 4;
this.maxHealth = opts.maxHealth || this.health;
this.name = opts.name || "THUG";
this.x = opts.x || this.canvas.width - 50;
this.y = opts.y || 0;
// just to control the scaling of an image
this.scale = 0.5;
this.stateImg = this.game.resources[this.name];
this.wasDamagedOnPreviousFrame = false;
this.frame = 0;
};
Enemy.prototype.shoot = function() {
var self = this;
var knife = this.game.resources.KNIFE;
var projectile = new Projectile(this.game);
projectile.name = "KNIFE";
projectile.damage = 1;
projectile.x = this.x - knife.width * this.scale / 2;
// so knifes height is divided by 4 because, 2 is for center, and another 2 is for 0.5 scale
projectile.y = this.y + (this.stateImg.height * this.scale / 2) - (knife.height * this.scale / 4);
projectile.update = function() {
this.ctx.drawImage(knife, this.x - this.game.cameraX, this.y, knife.width * self.scale / 2, knife.height * self.scale / 2);
this.x -= 10;
}
this.game.addProjectile(projectile);
}
Enemy.prototype.drawHealthbar = function() {
var healthbar = {
height: 5,
width: 100,
style: "red",
borderWidth: 2,
borderStyle: "black"
};
var x = this.x - this.game.cameraX;
x -= healthbar.width / 2; // to center the healthbar with the X of the character
x += this.stateImg.width * this.scale / 2; // to center the healthbar with the X of characters center
var y = this.y - (healthbar.height + healthbar.borderWidth * 2) - 5;
var width = healthbar.width * this.health / this.maxHealth; // get the width for current health
var height = healthbar.height;
var borderX = x - healthbar.borderWidth;
var borderY = y - healthbar.borderWidth;
var borderWidth = healthbar.width + healthbar.borderWidth * 2;
var borderHeight = healthbar.height + healthbar.borderWidth * 2;
this.ctx.fillStyle = healthbar.borderStyle;
this.ctx.fillRect(borderX, borderY, borderWidth, borderHeight);
this.ctx.fillStyle = healthbar.style;
this.ctx.fillRect(x, y, width, height);
}
Enemy.prototype.update = function() {
var img = this.game.resources[this.name];
this.stateImg = img;
if (this.health <= 0) {
this.remove();
}
this.drawHealthbar();
var x = this.x - this.game.cameraX;
var y = this.y;
var width = img.width * this.scale;
var height = img.height * this.scale;
this.ctx.save();
this.ctx.scale(-1, 1);
this.ctx.drawImage(this.game.resources[this.name], (x + width) * -1, y, width, height);
this.ctx.restore();
if (this.wasDamagedOnPreviousFrame) {
this.wasDamagedOnPreviousFrame = false;
this.ctx.fillStyle = "rgba(255, 0, 0, 0.2)";
this.ctx.fillRect(x, y, width, height);
}
var isInScreen = this.x - this.game.cameraX <= this.canvas.width;
if (this.frame % 100 === 0 && isInScreen) {
this.shoot();
}
this.frame++;
}
Enemy.prototype.remove = function() {
this.game.score++;
this.game.spiderman.web += this.maxHealth;
this.game.removeEnemy(this);
}
Enemy.prototype.handleHitWithProjectile = function(projectile) {
if (projectile.name == "WEB") {
this.health -= projectile.damage;
this.wasDamagedOnPreviousFrame = true;
@}
}
window.SpidermanGame = SpidermanGame;
window.Projectile = Projectile;
window.SpiderMan = SpiderMan;
window.Enemy = Enemy;
window.Roof = Roof;
})(window, document);
(function(window, document) {
// change this to relative path (relative to script) or to absolute path
// where the audio, fonts and images folders are located
// (include a slash at the end)
var RESOURCES_FOLDER_PATH = "";
var requestAnimationFrame = (function() {
if (window.requestAnimationFrame) return window.requestAnimationFrame;
if (window.oRequestAnimationFrame) return window.oRequestAnimationFrame;
if (window.msRequestAnimationFrame) return window.msRequestAnimationFrame;
if (window.mozRequestAnimationFrame) return window.mozRequestAnimationFrame;
return function(callback) {
setTimeout(callback, 1000 / 60);
}
})();
window.requestAnimFrame = requestAnimationFrame;
var link = document.createElement("link");
link.setAttribute("rel", "stylesheet");
link.setAttribute("href", RESOURCES_FOLDER_PATH + "css/spiderman-game.css");
document.head.appendChild(link);
var RESOURCES = {
"JUMP" : "images/jump.png",
"RUNNING_CHANGE_STEP": "images/running-change-step.png",
"RUNNING_LEFT_STEP" : "images/running-left-step.png",
"RUNNING_RIGHT_STEP" : "images/running-right-step.png",
"SHOOT_CHANGE_STEP" : "images/shoot-change-step.png",
"SHOOT_JUMP" : "images/shoot-jump.png",
"SHOOT_LEFT-STEP" : "images/shoot-left-step.png",
"SHOOT_RIGHT-STEP" : "images/shoot-right-step.png",
"SHOOT" : "images/shoot.png",
"SLIDE" : "images/slide.png",
"STANDING" : "images/standing.png",
"WEB_PROJECTILE" : "images/web.png",
"BACKGROUND" : "images/background.jpg",
"ROOF" : "images/wall.jpg",
"BUILDING" : "images/building.png",
"SPIDER_HEAD" : "images/spider-head.png",
"HEART" : "images/heart.png",
"VENOM" : "images/venom.png",
"THUG" : "images/thug.png",
"KNIFE" : "images/knife.png",
};
var AUDIO_RESOURCES = {
"AMAZING_SPIDER_MAN_2" : new Audio(RESOURCES_FOLDER_PATH + "audio/amazing-spider-man-2.mp3"),
"FRIENDLY_SPIDERMAN" : new Audio(RESOURCES_FOLDER_PATH + "audio/60-theme-song.mp3"),
"MOVIE_THEME" : new Audio(RESOURCES_FOLDER_PATH + "audio/old-theme.mp3"),
"ANIMATED_SERIES" : new Audio(RESOURCES_FOLDER_PATH + "audio/animated-series-theme.mp3"),
"SHOOT" : new Audio(RESOURCES_FOLDER_PATH + "audio/shooting-web.mp3"),
};
var AUDIO_LOOP = [
"AMAZING_SPIDER_MAN_2",
"FRIENDLY_SPIDERMAN",
"MOVIE_THEME",
"ANIMATED_SERIES",
];
var KEY = {
ARROW_LEFT: 37,
ARROW_UP: 38,
ARROW_RIGHT: 39,
ARROW_DOWN: 40,
SPACEBAR: 32,
A: 65,
S: 87,
D: 68,
W: 87,
ESC: 27,
};
var DIRECTION = {
RIGHT: 1,
LEFT: -1,
}
function SpidermanGame(opts) {
var options = {
canvas: "canvas",
score: 0,
muted: false,
soundEffects: true,
};
}
SpidermanGame.prototype.paused = false;
SpidermanGame.prototype.initialized = false;
SpidermanGame.prototype.soundEffects = true;
SpidermanGame.prototype.escapeKey = false;
SpidermanGame.prototype.muted = false;
SpidermanGame.prototype.slowmotion = false;
SpidermanGame.prototype.load = function() {
if (this.initialized) return false;
}
SpidermanGame.prototype.setSlowmotion = function(slowmo) {
if (slowmo) {
this.slowmotion = true;
}
SpidermanGame.prototype.mute = function() {
this.muted = true;
}
SpidermanGame.prototype.unmute = function() {
this.muted = false;
}
SpidermanGame.prototype.showPauseMenu = function() {
if (this.gameoverMenu.style.display == "block") return;
var pauseMenu = this.pauseMenu;
}
SpidermanGame.prototype.showGameoverMenu = function() {
var gameoverMenu = this.gameoverMenu;
this.gameoverMenu.querySelector(".spiderman-game-score").innerHTML = this.score;
}
SpidermanGame.prototype.pause = function() {
this.paused = true;
this.showPauseMenu();
};
SpidermanGame.prototype.unpause = function() {
this.paused = false;
}
SpidermanGame.prototype.playSound = function(audio, clone, currentTime) {
audio = audio && audio.play ? audio : AUDIO_RESOURCES[audio];
};
SpidermanGame.prototype.pauseSound = function(audio) {
if (audio && audio.pause) {
return audio.pause();
}
}
SpidermanGame.prototype.drawBackground = function() {
var background = this.resources.BACKGROUND;
var backgroundWidth = background.width;
var backgroundHeight = background.height;
}
SpidermanGame.prototype.drawRoofs = function() {
var roofs = this.scene.roofs;
}
SpidermanGame.prototype.drawEnemies = function() {
var enemies = this.scene.enemies;
}
SpidermanGame.prototype.update = function() {
if (this.paused) return;
if (this.gameIsOver) return;
}
SpidermanGame.prototype.addProjectile = function(projectile) {
if (projectile instanceof Projectile) {
this.scene.projectiles.push(projectile);
}
}
SpidermanGame.prototype.removeProjectile = function(projectile) {
var projectiles = this.scene.projectiles;
if (projectiles.indexOf(projectile) > -1) {
projectiles.splice(projectiles.indexOf(projectile), 1);
}
}
SpidermanGame.prototype.addEnemy = function(enemy) {
if (enemy instanceof Enemy) {
this.scene.enemies.push(enemy);
}
}
SpidermanGame.prototype.removeEnemy = function(enemy) {
var enemies = this.scene.enemies;
if (enemies.indexOf(enemy) > -1) {
enemies.splice(enemies.indexOf(enemy), 1);
}
}
SpidermanGame.prototype.addRoof = function(roof) {
if (roof instanceof Roof) {
this.scene.roofs.push(roof);
}
}
SpidermanGame.prototype.removeRoof = function(roof) {
var roofs = this.scene.roofs;
if (roofs.indexOf(roof) > -1) {
roofs.splice(roofs.indexOf(roof), 1);
}
}
// checks if given point is roof
SpidermanGame.prototype.isRoofAtPoint = function(x, y) {
x -= this.cameraX; // to move point relative to canvas
for (var i = 0; i < this.scene.roofs.length; i++) {
var roof = this.scene.roofs[i];
}
SpidermanGame.prototype.isCharacterAtPoint = function(x, y) {
// enemies + spiderman
var characters = this.scene.enemies.concat(this.spiderman);
x -= this.cameraX;
}
SpidermanGame.prototype.restart = function() {
var roof = new Roof(this);
roof.x = 0;
}
SpidermanGame.prototype.gameover = function() {
this.gameIsOver = true;
}
function SpiderMan(game) {
this.game = game;
this.canvas = game.canvas;
this.ctx = game.ctx;
this.name = "SPIDER_MAN";
this.x = 0;
this.y = 0;
this.states = ["STANDING"];
this.scale = 0.5;
this.keydowns = [];
this.health = 5;
this.maxHealth = 5;
}
SpiderMan.prototype.keyIsDown = function(keyCode) {
return this.keydowns.indexOf(keyCode) > -1;
}
SpiderMan.prototype.hasState = function(state) {
return this.states.indexOf(state) > -1;
}
SpiderMan.prototype.hasStates = function(states) {
states = states.split(" ");
var hasStates = true;
for (var i = 0; i < states.length; i++) {
hasState = hasState && this.hasState(states[i]);
}
return hasStates;
}
SpiderMan.prototype.addState = function(state) {
if (this.hasState(state) === false) this.states.push(state);
}
SpiderMan.prototype.removeState = function(state) {
if (state instanceof Array) {
for (var i = 0; i < state.length; i++) {
this.removeState(state[i]);
}
}
}
SpiderMan.prototype.handleHitWithProjectile = function(projectile) {
if (projectile.name != "WEB") {
this.health -= projectile.damage;
this.wasDamagedOnPreviousFrame = true;
}
}
// returns the image to draw in position of spiderman
SpiderMan.prototype.stateImage = function() {
var state = "STANDING";
}
SpiderMan.prototype.keydown = function(keyCode) {
this.keydowns.push(keyCode);
}
SpiderMan.prototype.keyup = function(keyCode) {
this.runningFrame = 0;
}
SpiderMan.prototype.regenerate = function() {
// if this is 300th fps, regenerate
if (this.frame % this.regenerationSpeed === 0 && this.health < this.maxHealth) {
this.health = Math.round(this.health + 1);
}
}
SpiderMan.prototype.shoot = function(img) {
if (this.web <= 0) return;
}
SpiderMan.prototype.drawHealthbar = function() {
var heart = {
width: 25,
height: 25,
};
}
SpiderMan.prototype.drawWebbar = function() {
var img = this.game.resources.WEB_PROJECTILE;
var string = "X " + this.web;
this.ctx.fillStyle = "white";
this.ctx.font = "15px SpidermanGamePixelFont, Monospace, Arial";
this.ctx.textAlign = "start";
this.ctx.textBaseline = "top";
}
// function that gets called with global update function
SpiderMan.prototype.update = function() {
if (this.keyIsDown(KEY.ARROW_UP) && !this.hasState("FALL")) {
this.addState("JUMP");
}
if (this.keyIsDown(KEY.ARROW_RIGHT)) {
this.addState("RUNNING");
this.runningDirection = DIRECTION.RIGHT;
}
if (this.keyIsDown(KEY.ARROW_LEFT)) {
this.addState("RUNNING");
this.runningDirection = DIRECTION.LEFT;
}
if (this.keyIsDown(KEY.SPACEBAR)) {
this.addState("SHOOT");
}
}
function Projectile(game) {
this.x = 0;
this.y = 0;
}
Projectile.prototype.update = function() {
}
Projectile.prototype.remove = function() {
this.game.removeProjectile(this);
}
Projectile.prototype.handleHitWithCharacter = function() {
this.remove();
}
function Roof(game, x, y) {
this.game = game;
this.canvas = game.canvas;
this.ctx = game.ctx;
}
Roof.prototype.update = function() {
var renderX = this.x - this.game.cameraX;
var roof = this.game.resources.BUILDING;
}
function Enemy(game, opts) {
opts = opts || {};
};
Enemy.prototype.shoot = function() {
var self = this;
var knife = this.game.resources.KNIFE;
}
Enemy.prototype.drawHealthbar = function() {
var healthbar = {
height: 5,
width: 100,
style: "red",
borderWidth: 2,
borderStyle: "black"
};
}
Enemy.prototype.update = function() {
var img = this.game.resources[this.name];
this.stateImg = img;
}
Enemy.prototype.remove = function() {
this.game.score++;
this.game.spiderman.web += this.maxHealth;
this.game.removeEnemy(this);
}
Enemy.prototype.handleHitWithProjectile = function(projectile) {
if (projectile.name == "WEB") {
this.health -= projectile.damage;
this.wasDamagedOnPreviousFrame = true;
@}
}
window.SpidermanGame = SpidermanGame;
window.Projectile = Projectile;
window.SpiderMan = SpiderMan;
window.Enemy = Enemy;
window.Roof = Roof;
})(window, document);