Commit 5b10cc84 authored by Matthew Odle's avatar Matthew Odle

Merge branch 'two-player-mode' into 'develop'

Two player mode

See merge request !18
parents 75a99b1e db5c10f7
/*jslint white: true */
var collisions = {
check : function() {
this.checkLaser(this.getLaserTargets());
this.checkPlayerVsEnemies(this.getPlayerEnemies());
Object.keys(lasers.lasers).forEach(key => {
let targets = this.getLaserTargets();
this.checkLaser(key, targets);
this.removeUsedLasers(key);
});
Object.keys(players.players).forEach(player =>
this.checkPlayerVsEnemies(player, this.getPlayerEnemies())
);
this.removeDestroyedTargets();
},
getLaserTargets : function() {
targets = [];
let targets = [];
targets.push(...mushrooms.mushrooms);
targets.push(...centipedes.centipedes);
targets.push(...intervalCreatures.worms);
......@@ -14,21 +20,19 @@ var collisions = {
targets.push(...spiders.spiders);
return targets;
},
checkLaser : function(targets) {
lasers.lasers.map(laser =>
targets.map(target => {
checkLaser : function(player, targets) {
lasers.lasers[player].forEach(laser =>
targets.forEach(target => {
if (!laser.remove && laser.crashWith(target)) {
this.processImpact(target);
laser.remove = true;
};
})
);
this.removeUsedLasers();
},
processImpact : function(target) {
this.damageTarget(target);
sounds.playImpactSound(target.type);
this.updateTargetAppearance(target);
},
damageTarget : function(target) {
target.hitPoints--;
......@@ -36,11 +40,6 @@ var collisions = {
this.processKill(target);
};
},
updateTargetAppearance(target) {
if (target.type == 'mushroom') {
target.height -= knobsAndLevers.mushrooms.side * 0.25;
};
},
processKill : function(target) {
metrics.addNewFloatingPoint(target.getMiddleX(), target.getMiddleY(), target.pointValue, "gain");
metrics.manageScore(target.pointValue);
......@@ -52,8 +51,8 @@ var collisions = {
centipedes.numberKilled += 1;
};
},
removeUsedLasers : function() {
lasers.lasers = lasers.lasers.filter(laser => !laser.remove);
removeUsedLasers : function(player) {
lasers.lasers[player] = lasers.lasers[player].filter(laser => !laser.remove);
},
getPlayerEnemies : function() {
targets = [];
......@@ -62,21 +61,21 @@ var collisions = {
targets.push(...intervalCreatures.flies);
return targets;
},
checkPlayerVsEnemies : function(targets) {
checkPlayerVsEnemies : function(player, targets) {
if (!knobsAndLevers.game.playerCollisionsEnabled) {
return;
};
targets.forEach(target => {
if (player.gamePiece.crashWith(target)) {
this.killPlayer();
if (player.crashWith(target)) {
this.killPlayer(player);
return;
};
});
},
killPlayer : function() {
player.died = true;
metrics.lives -= 1;
if (metrics.lives <= 0) {
players.died = true;
metrics.lives.player1 -= 1;
if (metrics.lives.player1 <= 0) {
game.gameOver = true;
return;
};
......
......@@ -16,6 +16,7 @@ function Component(args) {
};
if (args.extraArgs) {
this.type = args.extraArgs.type;
this.image = new Image();
if (args.extraArgs.speed) {
this.speedX = args.extraArgs.speed.x;
this.speedY = args.extraArgs.speed.y;
......@@ -27,17 +28,12 @@ function Component(args) {
};
let ctx = game.gameArea.context;
ctx.fillStyle = this.color;
if (this.type == "text") {
if (this.type == 'text') {
this.makeText(ctx);
} else if (this.type == "centipede") {
customComponents.makeACentipede(ctx, this.moveVertically, this);
// to draw the ship instead of a square
// } else if (this.type == "player") {
// let playerImage = new Image();
// playerImage.src = knobsAndLevers.mediaPath + "ship.png";
// ctx.drawImage(playerImage, this.x, this.y);
} else {
} else if (['background', 'laser'].includes(this.type)) {
this.makeARectangle(ctx);
} else {
customComponents.drawComponent(ctx, this);
};
};
this.stop = function() {
......@@ -115,54 +111,29 @@ function Component(args) {
};
var customComponents = {
makeACentipede : function(ctx, isVertical, baseObject) {
ctx.beginPath();
let vertices = this.getCentipedeVertices(isVertical, baseObject);
ctx.moveTo(vertices.x1, vertices.y1);
ctx.lineTo(vertices.x2, vertices.y2);
ctx.lineTo(vertices.x3, vertices.y3);
ctx.fill();
drawComponent : function(ctx, obj) {
let filename = this.imageFileNameGenerator[obj.type](obj);
if (!filename) {
throw 'filename error when drawing component';
};
obj.image.src = knobsAndLevers.mediaPath + filename + '.png';
ctx.drawImage(obj.image, obj.x, obj.y, obj.width, obj.height);
},
getCentipedeVertices : function(isVertical, baseObject) {
imageFileNameGenerator : {
'centipede' : function(obj) {return 'centipede-head-1-' + customComponents.getCentipedeDirection(obj);},
'player' : function(obj) {return obj.name ? obj.name : 'player1';},
'spider' : function(obj) {return 'spider-1';},
'mushroom' : function(obj) {return 'mushroom-' + (obj.poisoned ? 'poisoned-' : '') + obj.hitPoints;},
'worm' : function(obj) {return obj.speedX > 0 ? 'worm-2' : 'worm-1';},
'fly' : function(obj) {return 'flea-1';},
},
getCentipedeDirection : function(obj) {
let direction = '';
if (isVertical) {
direction = baseObject.directionY > 0 ? 'down' : 'up';
if (obj.moveVertically) {
direction = obj.directionY > 0 ? 'down' : 'up';
} else {
direction = baseObject.directionX > 0 ? 'right' : 'left';
direction = obj.directionX > 0 ? 'right' : 'left';
};
return new TriangleVertices(direction, baseObject);
return direction;
},
};
function TriangleVertices(direction, dimensions) {
with (dimensions) {
this.x1 = x;
this.y1 = y;
this.x2 = x;
this.y2 = y;
this.x3 = x;
this.y3 = y;
if (direction == 'up') {
this.y1 = y + height;
this.x2 = x + width / 2;
this.x3 = x + width;
this.y3 = y + height;
};
if (direction == 'down') {
this.x2 = x + width / 2;
this.y2 = y + height;
this.x3 = x + width;
};
if (direction == 'right') {
this.x2 = x + width;
this.y2 = y + height / 2;
this.y3 = y + height;
};
if (direction == 'left') {
this.x1 = x + width;
this.y2 = y + height / 2;
this.x3 = x + width;
this.y3 = y + height;
};
};
};
......@@ -11,7 +11,6 @@ var centipedes = {
this.update();
},
spawn : function() {
// TODO this is pretty complicated
this.determineSpawnPositions();
if (!this.eligibleToSpawn()) {
return;
......@@ -30,12 +29,11 @@ var centipedes = {
this.buildCentipedeStructure();
},
buildCentipedeStructure : function() {
let tier = knobsAndLevers.game.tier ? knobsAndLevers.game.tier : 1;
this.segments = tier + knobsAndLevers.centipede.maxNumber;
let tier = knobsAndLevers.game.tier;
this.segments = tier.current * 2 + knobsAndLevers.centipede.maxNumber;
this.positions = [];
let maxTier = knobsAndLevers.game.maxTier;
let upperLimit = tier < maxTier ? tier : maxTier;
while (this.positions.length < upperLimit) {
let upperLimit = tier.isMaxed ? tier.max : tier.current;
while (this.positions.length < supporting.getRandom(1, upperLimit)) {
this.positions.push(this.determineHorizontalPosition());
};
},
......@@ -66,12 +64,7 @@ var centipedes = {
return centipede;
},
cannotAdd : function(centipede) {
for (i = 0; i < this.centipedes.length; i += 1) {
if (this.centipedes[i].crashWith(centipede)) {
return true;
};
};
return false;
return this.centipedes.find(checkCentipede => checkCentipede.crashWith(centipede));
},
add : function(centipede) {
this.centipedes.push(centipede);
......@@ -92,7 +85,7 @@ var centipedes = {
this.numberKilled = 0;
},
determineDirections : function() {
this.centipedes.filter(centipede => !centipede.updated).map(centipede => {
this.centipedes.filter(centipede => !centipede.updated).forEach(centipede => {
this.moveDownwardInitially(centipede);
this.checkYDirectionInPlayerArea(centipede);
this.checkHorizonalCollisions(centipede);
......@@ -112,7 +105,7 @@ var centipedes = {
if (centipede.getBottom() > game.gameArea.canvas.height) {
centipede.reverseDirectionY = true;
centipede.poisoned = false;
} else if (centipede.getTop() < player.topLimit && centipede.distanceMovedFromBottom > 0) {
} else if (centipede.getTop() < knobsAndLevers.player.topLimit && centipede.distanceMovedFromBottom > 0) {
centipede.reverseDirectionY = true;
centipede.distanceMovedFromBottom = 0;
};
......
/*jslint white: true */
var displayObjectPrototype = {
manage : function() {
this.spawn();
this.update();
this.clearOutsideCanvas();
},
spawn : function() {
// look we're spawning things
},
update : function() {
// look we're updating things
},
clearOutsideCanvas : function() {
// define me in inheriting object
},
};
......@@ -29,12 +29,17 @@ var intervalCreatures = {
};
},
spawn : function(creature) {
this.setMax(creature);
if (this[creature].length >= knobsAndLevers[creature].maxNumber) {
return;
};
this.executeConstructorFunctions(creature);
this.make(creature);
},
setMax : function(creature) {
let tier = knobsAndLevers.game.tier;
knobsAndLevers[creature].maxNumber = tier.isMaxed ? tier.max : tier.current;
},
make : function(creature) {
let spawnedCreature = new Component(knobsAndLevers[creature].args);
let pointValue = knobsAndLevers[creature].pointValue;
......@@ -83,7 +88,7 @@ var intervalCreatures = {
changeMushrooms : function(creature) {
mushrooms.mushrooms.forEach(mushroom => {
if (creature.crashWithMiddle(mushroom)) {
mushroom.color = 'purple';
mushroom.color = 'black';
mushroom.poisoned = true;
};
});
......
/*jslint white: true */
var lasers = Object.create(displayObjectPrototype, {
lasers : {
value : [],
writable : true,
enumerable : true
},
spawn : {
value : function() {
if (!this.eligibleToSpawn()) {
var lasers = {
lasers : {player1 : [], player2 : []},
manage : function() {
Array.from(Object.keys(this.lasers)).forEach(key => {
let player = players.players[key];
if (!player) {
return;
};
this.add(this.make());
sounds.playAvailableLaserSound();
},
writable : false,
enumerable : true
this.spawn(player);
this.update(key);
this.clearOutsideCanvas(key);
});
},
eligibleToSpawn : {
value : function() {
let eligible = this.lasers.length < knobsAndLevers.laser.quantity.value
&& supporting.everyinterval(
game.gameArea.frameNo, knobsAndLevers.laser.interval
)
&& controls.isFiring();
return eligible;
},
writable : false,
enumerable : true
spawn : function(player) {
if (!this.eligibleToSpawn(player)) {
return;
};
this.add(player);
sounds.playAvailableLaserSound();
},
make : {
value : function() {
let laserArgs = knobsAndLevers.laser.args;
laserArgs.extraArgs.speed.y = -1 * knobsAndLevers.laser.speed.value;
if (player.gamePiece == undefined) {
throw('player.gamePiece is undefined');
};
laserArgs.x = player.gamePiece.x + player.gamePiece.width / 2;
laserArgs.y = player.gamePiece.y;
return new Component(laserArgs);
},
writable : true,
enumerable : true,
eligibleToSpawn : function(player) {
let eligible = this.lasers[player.name].length < knobsAndLevers.laser.quantity.value
&& supporting.everyinterval(
game.gameArea.frameNo, knobsAndLevers.laser.interval
)
&& controls.isFiring(player);
return eligible;
},
add : function(player) {
this.lasers[player.name].push(this.make(player));
},
make : function(player) {
let laserArgs = knobsAndLevers.laser.args;
laserArgs.extraArgs.speed.y = -1 * knobsAndLevers.laser.speed.value;
laserArgs.x = player.x + player.width / 2;
laserArgs.y = player.y;
return new Component(laserArgs);
},
add : {
value : function() {
this.lasers.push(this.make());
},
writable : true,
enumerable : true,
update : function(playerName) {
for (i = 0; i < this.lasers[playerName].length; i += 1) {
this.lasers[playerName][i].y += this.lasers[playerName][i].speedY;
this.lasers[playerName][i].update();
}
},
update : {
value : function() {
for (i = 0; i < this.lasers.length; i += 1) {
this.lasers[i].y += this.lasers[i].speedY;
this.lasers[i].update();
}
},
writable : false,
enumerable : true
clearOutsideCanvas : function(playerName) {
this.lasers[playerName] = this.lasers[playerName].filter(laser => laser.y > 0);
},
clearOutsideCanvas : {
value : function() {
this.lasers = this.lasers.filter(laser => laser.y > 0);
},
writable : false,
enumerable : true
clear : function() {
this.lasers = {player1 : [], player2 : []};
},
clear : {
value : function() {
this.lasers = [];
},
writable : true,
enumerable : true
}
});
};
......@@ -2,7 +2,7 @@
var metrics = {
floatingPoints : [],
floatingPointCycleDuration : 50,
lives : 0,
lives : {player1 : 0, player2 : 0},
extraLivesGained : 0,
currentLevel : {},
score : {},
......@@ -12,9 +12,15 @@ var metrics = {
let scoreParams = Object.assign({}, knobsAndLevers.text.baseParams);
scoreParams.x = 100;
scoreParams.y = knobsAndLevers.text.gameInfoHeight;
this.score = new Component(scoreParams);
this.score.value = 0;
this.lives = knobsAndLevers.player.defaultLives;
// this.score = new Component(scoreParams);
// this.score.value = 0;
this.score.player1 = new Component(scoreParams);
this.score.player1.value = 0;
scoreParams.x = 200;
this.score.player2 = new Component(scoreParams);
this.score.player2.value = 0;
this.lives.player1 = knobsAndLevers.player.defaultLives;
this.lives.player2 = knobsAndLevers.player.defaultLives;
this.livesMarker = Object.assign({}, templates.marker);
console.log("metrics initialized");
},
......@@ -23,24 +29,27 @@ var metrics = {
this.manageTier();
},
changeScore : function(change) {
this.score.value += change;
this.score.value = this.score.value < 0 ? 0 : this.score.value;
this.score.player1.value += change;
this.score.player1.value = this.score.player1.value < 0 ? 0 : this.score.player1.value;
},
manageTier : function() {
this.setTier();
let maxTier = knobsAndLevers.game.maxTier;
let tier = knobsAndLevers.game.tier;
knobsAndLevers.spider.maxNumber = tier < maxTier ? tier + 1 : maxTier;
knobsAndLevers.flies.maxNumber = tier < maxTier ? tier + 1 : maxTier;
if (this.extraLivesGained < tier) {
this.lives += 1;
this.manageLives();
},
setTier : function() {
let newTier = Math.floor((this.score.player1.value + 1) / knobsAndLevers.game.tier.incrementScore) + 1;
if (!newTier) {
throw('problem calculating tier');
};
knobsAndLevers.game.tier.update(newTier);
},
manageLives : function() {
if (this.extraLivesGained < knobsAndLevers.game.tier.current - 1) {
this.lives.player1 += 1;
this.extraLivesGained += 1;
sounds.playTierChangeSound();
};
},
setTier : function() {
knobsAndLevers.game.tier = Math.floor((this.score.value + 1) / knobsAndLevers.game.incrementThingsScore);
},
addNewFloatingPoint : function(x, y, points, action) {
let newPoint = this.getNewPoint(x, y);
newPoint.color = action == 'lose' ? 'red' : 'black';
......@@ -69,9 +78,11 @@ var metrics = {
};
},
reset : function() {
this.lives = knobsAndLevers.player.defaultLives;
this.lives.player1 = knobsAndLevers.player.defaultLives;
this.lives.player2 = knobsAndLevers.player.defaultLives;
this.currentLevel = 1;
this.lastScore = this.score.value;
this.score.value = 0;
this.lastScore = this.score.player1.value;
this.score.player1.value = 0;
this.score.player2.value = 0;
},
};
......@@ -32,22 +32,14 @@ var mushrooms = {
color = color ? color : knobsAndLevers.mushrooms.color;
coordinates.x = supporting.getClosest(game.gameArea.xVertices, coordinates.x);
coordinates.y = supporting.getClosest(game.gameArea.yVertices, coordinates.y);
// this works, and should be more efficient than creating the component
// and running it through the collisions.withMushrooms check
if (!this.willOverlap(coordinates)) {
this.mushrooms.push(this.generate(coordinates, color));
let mushroom = this.generate(coordinates, color);
if (collisions.withMushrooms(mushroom) || this.collidesWithPlayers(mushroom)) {
return;
};
this.mushrooms.push(mushroom);
},
willOverlap : function(coordinates) {
let scaleFactor = knobsAndLevers.mushrooms.scaleFactor;
return this.mushrooms.find(mushroom =>
mushroom.hitPoints > 0
&& mushroom.x - scaleFactor < coordinates.x + 5
&& mushroom.x - scaleFactor > coordinates.x - 5
&& mushroom.y - scaleFactor < coordinates.y + 5
&& mushroom.y - scaleFactor > coordinates.y - 5
);
collidesWithPlayers : function(mushroom) {
return Object.keys(players.players).find(key => players.players[key].crashWith(mushroom));
},
generate : function(coordinates, color) {
let mushroomArgs = {
......
/*jslint white: true */
var player = {
activeDirection : undefined,
boundaries : {},
died : false,
eligibleDirections : {},
watchPositions : {
'up' : ['belowTop'],
'right' : ['insideRight'],
'down' : ['aboveBottom'],
'left' : ['insideLeft'],
'upRight' : ['belowTop', 'insideRight'],
'downRight' : ['aboveBottom', 'insideRight'],
'downLeft' : ['aboveBottom', 'insideLeft'],
'upLeft' : ['belowTop', 'insideLeft'],
},
init : function() {
this.areaHeight = knobsAndLevers.player.areaHeight;
this.topLimit = knobsAndLevers.player.topLimit;
let gamePieceArgs = {
width: knobsAndLevers.player.width,
height : knobsAndLevers.player.height,
color : "red",
x : knobsAndLevers.player.startX,
y : knobsAndLevers.player.startY,
extraArgs : {type : "player", speed : {x : 0, y : 0}}
};
this.gamePiece = new Component(gamePieceArgs);
this.calculateStartingArea();
console.log("player initialized");
},
calculateStartingArea : function() {
this.gamePieceStartingArea = new Component(
{
x : knobsAndLevers.player.startX - knobsAndLevers.player.width * 5,
y : knobsAndLevers.player.topLimit,
width : knobsAndLevers.player.width * 10,
height : knobsAndLevers.canvas.height - knobsAndLevers.player.topLimit,
color : "orange",
extraArgs : {type : "startingArea"},
}
);
},
manage : function() {
this.move();
this.update();
},
update : function() {
this.gamePiece.update();
},
reset : function() {
this.gamePiece.x = knobsAndLevers.player.startX;
this.gamePiece.y = knobsAndLevers.player.startY;
this.removeMushroomsFromStartingArea();
},
// TODO move this to mushrooms.js
removeMushroomsFromStartingArea : function() {
mushrooms.mushrooms = mushrooms.mushrooms.filter(mushroom => !mushroom.crashWith(this.gamePieceStartingArea));
},
move : function() {
this.stop();
this.setBoundaries();
this.determineEligibleDirections();
this.moveTheThing(controls.getPositionModifiers(this.boundaries, knobsAndLevers.player.speed.value, this.eligibleDirections));
},
stop : function() {
this.gamePiece.speedX = 0;
this.gamePiece.speedY = 0;
},
setBoundaries : function() {
this.boundaries.belowTop = this.gamePiece.getTop() > player.topLimit;
this.boundaries.insideRight = this.gamePiece.getRight() < game.gameArea.canvas.width;
this.boundaries.aboveBottom = this.gamePiece.getBottom() < game.gameArea.canvas.height;
this.boundaries.insideLeft = this.gamePiece.getLeft() > 0;
},
determineEligibleDirections : function() {
this.setEligibleDirectionsToDefault();
Array.from(Object.keys(this.watchPositions)).forEach(direction => {
this.watchPositions[direction].forEach(playerPosition =>
this.eligibleDirections[direction] = this.boundaries[playerPosition] && this.eligibleDirections[direction]
);
});
},
setEligibleDirectionsToDefault : function() {
Array.from(Object.keys(this.watchPositions)).forEach(direction => {
this.eligibleDirections[direction] = true;
});
},
moveTheThing : function(speed) {
if (!speed) {
return;
};
this.updatePosition(speed);
if (collisions.withMushrooms(this.gamePiece)) {
this.revertPosition(speed);
};
},
updatePosition : function(modifier) {
this.gamePiece.speedX = modifier.x ? modifier.x : this.gamePiece.speedX;
this.gamePiece.speedY = modifier.y ? modifier.y : this.gamePiece.speedY;
this.gamePiece.newPos();
},
revertPosition : function(modifier) {
this.gamePiece.speedX = -1 * (modifier.x ? modifier.x : this.gamePiece.speedX);
this.gamePiece.speedY = -1 * (modifier.y ? modifier.y : this.gamePiece.speedY);
this.gamePiece.newPos();
},
};
/*jslint white: true */
var playerConstants = {
watchPositions : {
'up' : ['belowTop'],
'right' : ['insideRight'],
'down' : ['aboveBottom'],
'left' : ['insideLeft'],
'upRight' : ['belowTop', 'insideRight'],
'downRight' : ['aboveBottom', 'insideRight'],
'downLeft' : ['aboveBottom', 'insideLeft'],
'upLeft' : ['belowTop', 'insideLeft'],
},
eligibleDirections : {
'up' : true,
'right' : true,
'down' : true,